#pragma once
#include "abstract.hpp"
#include <muduo/net/TcpServer.h>
#include <muduo/net/TcpClient.h>
#include <muduo/net/InetAddress.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/net/Buffer.h>
#include <iostream>
#include "detail.hpp"
#include "message.hpp"
#include <mutex>

namespace qiangsir
{
    class MuduoBuffer : public BaseBuffer
    {
    public:
        using ptr = std::shared_ptr<MuduoBuffer>;
        MuduoBuffer(muduo::net::Buffer *buf) : _buf(buf)
        {
        }
        virtual size_t readableSize() override
        {
            return _buf->readableBytes();
        }
        virtual int32_t peekInt32() override
        {
            return _buf->peekInt32();
        }
        virtual void retrieveInt32() override
        {
            _buf->retrieveInt32();
        }
        virtual int32_t readInt32() override
        {
            return _buf->readInt32();
        }
        virtual std::string retrieveAsString(size_t len) override
        {
            return _buf->retrieveAsString(len);
        }

    protected:
        muduo::net::Buffer *_buf;
    };

    class BaseBufferFactory
    {
    public:
        template <typename... Args>
        static BaseBuffer::ptr create(Args &&... args)
        {
            return std::make_shared<MuduoBuffer>(std::forward<Args>(args)...);
        }
    };

    class LVProtocol : public BaseProtocal
    {
    public:
        using ptr = std::shared_ptr<LVProtocol>;
        virtual bool canProcessed(const BaseBuffer::ptr &buf) override
        {
            if (buf->readableSize() < lenfieldsize)
            {
                return false;
            }
            int32_t total_len = buf->peekInt32();
            if (buf->readableSize() < total_len + lenfieldsize)
            {
                return false;
            }
            return true;
        }

        virtual bool onMessage(const BaseBuffer::ptr &buf, BaseMessage::ptr &message) override
        {
            // |--Len--|--mtype--|--idlen--|--id--|--body--|
            int32_t total_len = buf->readInt32();  // 读取总长度
            MType mtype = (MType)buf->readInt32(); // 读取类型
            int32_t id_len = buf->readInt32();     // 读取id长度
            int32_t body_len = total_len - typefieldsize - idfieldsize - id_len;
            std::string id = buf->retrieveAsString(id_len);
            std::string body = buf->retrieveAsString(body_len);
            message = BaseMessageFactory::create(mtype);
            if (message.get() == nullptr)
            {
                ELOG("消息类型错误,创建失败");
                return false;
            }
            bool ret = message->deSerialize(body);
            if (ret == false)
            {
                ELOG("消息正文反序列化失败");
                return false;
            }
            message->setId(id);
            message->setType(mtype);
            return true;
        }
        virtual std::string serialize(const BaseMessage::ptr &message) override
        {
            // |--Len--|--mtype--|--idlen--|--id--|--body--|
            std::string body = message->serialize();
            std::string id = message->id();
            int32_t idlen = htonl(id.size());
            int32_t bodylen = body.size();
            auto mtype = htonl((int32_t)message->mtype());
            int32_t total_len = typefieldsize + idfieldsize + id.size() + body.size();
            int32_t ntotal_len = htonl(total_len);
            std::string result;
            result.reserve(total_len + lenfieldsize);
            result.append((char *)&ntotal_len, lenfieldsize);
            result.append((char *)&mtype, typefieldsize);
            result.append((char *)&idlen, lenfieldsize);
            result.append(id);
            result.append(body);
            return result;
        }

    private:
        const size_t lenfieldsize = 4;
        const size_t typefieldsize = 4;
        const size_t idfieldsize = 4;
    };
    class ProtocolFactory
    {
    public:
        template <typename... Args>
        static BaseProtocal::ptr create(Args &&...args)
        {
            return std::make_shared<LVProtocol>(std::forward<Args>(args)...);
        }
    };

    class MuduoConnection : public BaseConnection
    {
    public:
        using ptr = std::shared_ptr<MuduoConnection>;
        MuduoConnection(const muduo::net::TcpConnectionPtr &con,
                        const BaseProtocal::ptr &protocol)
            : _con(con), _protocol(protocol)
        {
        }
        virtual void send(const BaseMessage::ptr &msg) override
        {
            std::string body = _protocol->serialize(msg);
            _con->send(body);
        }
        virtual void shutDown() override
        {
            _con->shutdown();
        }
        virtual bool connected() override
        {
            return _con->connected();
        }

    private:
        muduo::net::TcpConnectionPtr _con;
        BaseProtocal::ptr _protocol;
    };

    class ConnectionFactory
    {
    public:
        template <typename... Args>
        static BaseConnection::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoConnection>(std::forward<Args>(args)...);
        }
    };

    class MuduoServer : public BaseServer
    {
    public:
        using ptr = std::shared_ptr<MuduoServer>;
        MuduoServer(int port) : _server(&_loop, muduo::net::InetAddress("0.0.0.0", port), "Server",
                                        muduo::net::TcpServer::kNoReusePort),
                                _protocol(ProtocolFactory::create())
        {
        }
        virtual void start() override
        {
            _server.setConnectionCallback(std::bind(&MuduoServer::OnConnection, this,
                                                    std::placeholders::_1));
            _server.setMessageCallback(std::bind(&MuduoServer::OnMessage, this,
                                                 std::placeholders::_1, std::placeholders::_2,
                                                 std::placeholders::_3));
            _server.start(); // 开始监控
            _loop.loop();    // 开始循环
        }

    private:
        // 1.连接管理
        void OnConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            BaseConnection::ptr muduo_conn;
            if (conn->connected())
            {
                std::cout << "连接建立" << std::endl;
                muduo_conn = ConnectionFactory::create(conn, _protocol);
                {
                    std::unique_lock<std::mutex> ulock(_mutex);
                    _conns.insert(std::make_pair(conn, muduo_conn));
                }
            }
            else
            {
                std::cout << "连接断开" << std::endl;
                {
                    std::unique_lock<std::mutex> ulock(_mutex);
                    auto pos = _conns.find(conn);
                    if (pos == _conns.end())
                    {
                        return;
                    }
                    muduo_conn = pos->second;
                    _conns.erase(conn);
                }
            }
            if (_cb_close)
                _cb_close(muduo_conn);
        }
        // 2.信息管理
        void OnMessage(const muduo::net::TcpConnectionPtr &conn,
                       muduo::net::Buffer *buf, muduo::Timestamp)
        {
            DLOG("有数据来到，正在处理");
            auto basebuffer = BaseBufferFactory::create(buf);
            while (1)
            {
                if (_protocol->canProcessed(basebuffer) == false)
                {
                    if (basebuffer->readableSize() > maxDataSize)
                    {
                        ELOG("数据过大");
                        return;
                    }
                    break;
                }
                ELOG("数据可处理");
                BaseMessage::ptr msg;
                bool ret = _protocol->onMessage(basebuffer, msg);
                if (ret == false)
                {
                    conn->shutdown();
                    ELOG("数据出错");
                    return;
                }
                BaseConnection::ptr base_conn;
                {
                    std::unique_lock<std::mutex> ulock(_mutex);
                    auto pos = _conns.find(conn);
                    if (pos == _conns.end())
                    {
                        return;
                    }
                    base_conn = pos->second;
                }
                if (_cb_message)
                    _cb_message(base_conn, msg);
            }
        }
        const size_t maxDataSize = (1 << 16);
        muduo::net::EventLoop _loop;   // 设置事件循环
        muduo::net::TcpServer _server; // 开启服务器
        BaseProtocal::ptr _protocol;
        std::mutex _mutex;
        std::unordered_map<muduo::net::TcpConnectionPtr, BaseConnection::ptr> _conns;
    };
    class ServerFactory
    {
    public:
        template <typename... Args>
        static BaseServer::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoServer>(std::forward<Args>(args)...);
        }
    };

    class MuduoClient : public BaseClient
    {
    public:
        using ptr = std::shared_ptr<MuduoClient>;
        MuduoClient(const std::string &sip, int sport)
            : _baseloop(_threadloop.startLoop()),
              _countdown(1),
              _protocol(ProtocolFactory::create()),
              _client(_baseloop, muduo::net::InetAddress(sip, sport), "DictClient")
        {
        }

        virtual void connect() override
        {
            // 注册函数
            _client.setConnectionCallback(std::bind(&MuduoClient::OnConnection, this, std::placeholders::_1));

            _client.setMessageCallback(std::bind(&MuduoClient::OnMessage, this,
                                                 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            // 连接服务器
            _client.connect();
            _countdown.wait();
        }
        virtual void shutDown() override
        {
            _client.disconnect();
        }
        virtual BaseConnection::ptr connection() override
        {
            return _conn;
        }
        virtual bool send(const BaseMessage::ptr &msg) override
        {
            if (_conn->connected() == false)
            {
                ELOG("连接断开");
                return false;
            }
            _conn->send(msg);
            return true;
        }
        virtual bool connected() override
        {
            return (_conn && _conn->connected());
        }

    private:
        // 1.连接管理
        void OnConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                std::cout << "连接成功" << std::endl;
                _countdown.countDown(); // 计数--，为0时自动激活 count--
                _conn = ConnectionFactory::create(conn, _protocol);
            }
            else
            {
                std::cout << "连接断开" << std::endl;
                _conn.reset();
            }
        }
        // 2.信息管理
        void OnMessage(const muduo::net::TcpConnectionPtr &conn,
                       muduo::net::Buffer *buf, muduo::Timestamp)
        {
            DLOG("数据到来，开始处理");
            BaseBuffer::ptr base_buf = BaseBufferFactory::create(buf);
            while (1)
            {
                if (_protocol->canProcessed(base_buf) == false)
                {
                    if (base_buf->readableSize() > maxDataSize)
                    {
                        ELOG("数据过大");
                    }
                    break;
                }
                BaseMessage::ptr msg;
                bool ret = _protocol->onMessage(base_buf, msg);
                if (ret == false)
                {
                    conn->shutdown();
                    ELOG("数据错误");
                    return;
                }
                if (_cb_message)
                    _cb_message(_conn, msg);
            }
        }

    private:
        const size_t maxDataSize = (1 << 16);
        BaseConnection::ptr _conn;
        BaseProtocal::ptr _protocol;
        muduo::CountDownLatch _countdown;
        muduo::net::EventLoopThread _threadloop;
        muduo::net::EventLoop *_baseloop;
        muduo::net::TcpClient _client;
    };
    class ClientFactory
    {
    public:
        template <typename... Args>
        static BaseClient::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoClient>(std::forward<Args>(args)...);
        }
    };
}